/*    file: G722Transcoder.cpp
 *    desc:
 * 
 * created: 2017-05-05
 *  author: chuanjiang.zh@qq.com
 * company: 
 */

#include "G722Transcoder.h"
#include "CLog.h"


namespace av
{



G722Transcoder::G722Transcoder():
	m_srcFormat(),
	m_outFormat(),
	m_decContext(),
	m_swrContext(),
	m_resample()
{

}

G722Transcoder::~G722Transcoder()
{
	close();
}

bool G722Transcoder::open(const AudioFormat& srcFormat, const AudioFormat& outFormat)
{
	if (outFormat.m_codec != MEDIA_CODEC_G722)
	{
		return false;
	}

	m_srcFormat = srcFormat;

	m_outFormat.m_codec = MEDIA_CODEC_G722;
	m_outFormat.m_channels = 1;
	m_outFormat.m_samplerate = 16000;

    int rc = openDecoder();
	if (rc != 0)
	{
		return false;
	}

	if (!m_encoder.open())
	{
		close();
		return false;
	}

	int err = 0;
	m_resample = speex_resampler_init(m_outFormat.m_channels, m_srcFormat.m_samplerate, m_outFormat.m_samplerate, 10, &err);

	return true;
}

void G722Transcoder::close()
{
	closeDecoder();

	m_encoder.close();

	if (m_swrContext)
	{
		swr_close(m_swrContext);
		swr_free(&m_swrContext);
		m_swrContext = NULL;
	}

	if (m_resample)
	{
		speex_resampler_destroy(m_resample);
		m_resample = NULL;
	}
}

bool G722Transcoder::isOpen()
{
	return m_encoder.isOpen();
}

bool G722Transcoder::transcode(AVPacket* inPkt, AVPacket* outPkt)
{
	if (!m_decContext)
	{
		return false;
	}

	if (!inPkt)
	{
		return false;
	}

	m_outBuffer.ensure(inPkt->size);
	int outsize = 0;

	int ret = -1;
	AVPacket pkt;
	av_init_packet(&pkt);

	pkt.data = inPkt->data;
	pkt.size = inPkt->size;

	while (pkt.size > 0)
	{
		AVFramePtr frame(av_frame_alloc(), ffmpeg::AVFrameDeleter());

		int gotFrame = 0;
		int bytes = avcodec_decode_audio4(m_decContext, frame.get(), &gotFrame, &pkt);
		if (bytes <= 0)
		{
			break;
		}

		ret = 0;

		pkt.size -= bytes;
		pkt.data += bytes;

		if (gotFrame)
		{
			frame->channel_layout = av_get_default_channel_layout(frame->channels);

	        AVFramePtr outFrame(av_frame_alloc(), ffmpeg::AVFrameDeleter());

			outFrame->format = AV_SAMPLE_FMT_S16;
			outFrame->channels = m_outFormat.m_channels;
			outFrame->channel_layout = av_get_default_channel_layout(outFrame->channels);
			outFrame->sample_rate = m_outFormat.m_samplerate;
			outFrame->pts = frame->pts;

			int out_samples = av_rescale_rnd(frame->nb_samples, outFrame->sample_rate, frame->sample_rate, AV_ROUND_UP);
	        outFrame->nb_samples = out_samples;
	        av_frame_get_buffer(outFrame.get(), 1);

			int rc = resample(frame.get(), outFrame.get());
			if (rc > 0)
			{
				int length = m_encoder.encode(outFrame->data[0], outFrame->linesize[0], m_outBuffer.data() + outsize);
				outsize += length;
			}
		}
	}

	if (outsize <= 0)
	{
		return false;
	}

	ffmpeg::av_packet_copy_data(outPkt, m_outBuffer.data(), outsize);

	return true;
}

AudioFormat G722Transcoder::getOutFormat()
{
	return m_outFormat;
}


int G722Transcoder::openDecoder()
{
	AVCodecID codecID = (AVCodecID)m_srcFormat.m_codec;
	AVCodec* pCodec = avcodec_find_decoder(codecID);
	if (!pCodec)
	{
		return EBADF;
	}

	m_decContext = avcodec_alloc_context3(pCodec);

	m_decContext->refcounted_frames = 1;

	m_decContext->codec_type = AVMEDIA_TYPE_AUDIO;
	m_decContext->channels = m_srcFormat.m_channels;
	m_decContext->sample_rate = m_srcFormat.m_samplerate;

	if (m_srcFormat.m_config.size())
	{
		m_decContext->extradata = (uint8_t*)av_memdup(m_srcFormat.m_config.c_str(), m_srcFormat.m_config.size());
		m_decContext->extradata_size = m_srcFormat.m_config.size();
	}

	if (avcodec_open2(m_decContext, pCodec, NULL) < 0)
	{
		avcodec_close(m_decContext);
		av_free(m_decContext);
		m_decContext = NULL;
		return EBADF;
	}

	return 0;
}

void G722Transcoder::closeDecoder()
{
	if (m_decContext)
	{
		avcodec_close(m_decContext);
		av_free(m_decContext);
		m_decContext = NULL;
	}
}


int G722Transcoder::resample(AVFrame* inFrame, AVFrame* outFrame)
{
	spx_uint32_t in_len = inFrame->nb_samples;
	spx_uint32_t samples = outFrame->nb_samples;
	int rc = speex_resampler_process_int(m_resample, 0, (spx_int16_t*)inFrame->data[0], &in_len,
		(spx_int16_t*)outFrame->data[0], &samples);

	outFrame->nb_samples = samples;
	outFrame->linesize[0] = outFrame->nb_samples * av_get_bytes_per_sample((AVSampleFormat)outFrame->format);
	return samples;

	/*
    int rc = 0;
    if (m_swrContext == NULL)
    {
        m_swrContext = swr_alloc();

        rc = swr_config_frame(m_swrContext, outFrame, inFrame);

        rc = swr_init(m_swrContext);
        if (rc != 0)
        {
            CLog::error("CAudioTranscoder. swr_init failed. rc:%d\n", rc);
        }

        //rc = swr_is_initialized(m_swrContext);
    }
    else
    {
        int ret = swr_config_frame(m_swrContext, outFrame, inFrame);
    }

    int ret = swr_convert_frame(m_swrContext, outFrame, inFrame);
	if (ret == 0)
	{
		outFrame->linesize[0] = outFrame->nb_samples * av_get_bytes_per_sample((AVSampleFormat)outFrame->format);
	}
    return outFrame->nb_samples;
	*/
}


}
